/*
libndi
Copyright (C) 2020 VideoLAN

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <time.h>
#include <unistd.h>

#include <sys/types.h>
#ifdef _WIN32
#include <wspiapi.h>
#define sleep(x) Sleep((x) * 1000)
#else
#include <poll.h>
#include <sys/socket.h>
#include <netdb.h>
#endif

#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/fifo.h>
#include <libavcodec/avcodec.h>

#include "libndi.h"

AVCodec *codec;
AVCodecContext *avctx;
AVPacket *pkt;
AVFrame *frame;

void ndi_data_callback_func(ndi_data *ndi_data, void *user_data)
{
    int ret = 0;
    printf("Got data. Type: %s \n", ndi_data->data_type == NDI_DATA_VIDEO ? "video": "audio");

    if(ndi_data->data_type == NDI_DATA_VIDEO) {
        if(!avctx->codec_tag) {
            avctx->codec_tag = ndi_data->fourcc;
            avctx->width = ndi_data->width;
            avctx->height = ndi_data->height;

            if(avcodec_open2(avctx, codec, NULL) < 0) {
                fprintf(stderr, "Could not open codec\n");
                avctx->codec_tag = 0;
                goto end;
            }
        }

        av_new_packet(pkt, ndi_data->len);
        memcpy(pkt->data, ndi_data->data, ndi_data->len);
        ret = avcodec_send_packet(avctx, pkt);
        if (ret < 0) {
            fprintf(stderr, "Error sending a packet for decoding\n");
            goto end;
        }

        while (ret >= 0) {
            ret = avcodec_receive_frame(avctx, frame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                goto end;
            else if (ret < 0) {
                fprintf(stderr, "Error during decoding\n");
                goto end;
            }
        }
    }

end:
    av_packet_unref(pkt);
}

void ndi_mdns_callback_func(ndi_discovery_item_t *item, void *user_data)
{
    printf("Discovered Source: Name: %s IP: %s Port: %s \n", item->name, item->ip, item->port);
}

int main(int argc, char *argv[])
{
    int ret = 0;

    ndi_opts ndi_opts = {0};
    if(argc == 2) {
        if(!strcmp(argv[1], "list")) {
            ndi_discovery_ctx_t *ndi_discovery = libndi_discovery_init(ndi_mdns_callback_func, NULL);
            libndi_discovery_start(ndi_discovery);
            sleep(5);
            libndi_discovery_stop(ndi_discovery);
            libndi_discovery_destroy(ndi_discovery);
        }
    }
    else if(argc != 3) {
        printf("ndi example decode utility \n");
        printf("ndi list - list NDI sources \n");
        printf("ndi 192.168.1.1 1234 - connect and decode from IP\n");
    }
    else {
        ndi_ctx *ndi_ctx = libndi_init();
        codec = avcodec_find_decoder(AV_CODEC_ID_SPEEDHQ);
        if(!codec) {
            fprintf(stderr, "SpeedHQ codec not found \n");
            return -1;
        }

        avctx = avcodec_alloc_context3(codec);
        if(!avctx) {
            fprintf(stderr, "Could not allocate AVCodecContext \n");
            return -1;
        }

        pkt = av_packet_alloc();
        if(!pkt) {
            fprintf(stderr, "Could not allocate AVPacket \n");
            ret = -1;
            goto end;
        }

        frame = av_frame_alloc();
        if(!frame) {
            fprintf(stderr, "Could not allocate AVFrame \n");
            ret = -1;
            goto end;
        }

        ndi_opts.ip = argv[1];
        ndi_opts.port = argv[2];
        ndi_opts.initial_tally_state = NDI_TALLY_LIVE;

        libndi_setup(ndi_ctx, &ndi_opts);

        libndi_receive_data(ndi_ctx, ndi_data_callback_func, NULL);

        libndi_close(ndi_ctx);

end:

        av_frame_free(&frame);
        av_packet_free(&pkt);
        avcodec_free_context(&avctx);
    }

    return ret;
}
